package io.jnet.client.client.impl;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import com.jfireframework.baseutil.exception.JustThrowException;
import io.jnet.client.build.ClientChannelContextBuilder;
import io.jnet.client.build.ClientChannelContextConfig;
import io.jnet.client.client.AioClient;
import io.jnet.common.api.ChannelContext;
import io.jnet.common.util.AioListener;

public abstract class AbstractClient implements AioClient
{
    private static final int                    connectTimeout = 10;
    protected final AsynchronousChannelGroup    channelGroup;
    protected final String                      serverIp;
    protected final int                         port;
    protected final AioListener                 aioListener;
    protected final ClientChannelContextBuilder clientChannelContextBuilder;
    protected ChannelContext                    clientChannelContext;
    
    public AbstractClient(ClientChannelContextBuilder clientChannelContextBuilder, AsynchronousChannelGroup channelGroup, String serverIp, int port, AioListener aioListener)
    {
        this.channelGroup = channelGroup;
        this.serverIp = serverIp;
        this.port = port;
        this.aioListener = aioListener;
        this.clientChannelContextBuilder = clientChannelContextBuilder;
    }
    
    @Override
    public void connect()
    {
        try
        {
            AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open(channelGroup);
            socketChannel.connect(new InetSocketAddress(serverIp, port)).get(connectTimeout, TimeUnit.SECONDS);
            ClientChannelContextConfig config = clientChannelContextBuilder.onConnect(socketChannel);
            clientChannelContext = parse(config, socketChannel, aioListener);
            clientChannelContext.registerRead();
        }
        catch (IOException | InterruptedException | ExecutionException | TimeoutException e)
        {
            throw new JustThrowException(e);
        }
    }
    
    protected abstract ChannelContext parse(ClientChannelContextConfig config, AsynchronousSocketChannel socketChannel, AioListener listener);
    
    @Override
    public void close()
    {
        clientChannelContext.close();
        clientChannelContext = null;
    }
    
    @Override
    public void write(Object packet) throws Throwable
    {
        clientChannelContext.push(packet, 0);
    }
}
